package it.traveldream.beans;

import it.traveldream.dto.HotelDTO;
import it.traveldream.dto.HotelDTO.HotelRoomType;
import it.traveldream.dto.LocationDTO;
import it.traveldream.dto.ProductDTO;
import it.traveldream.dto.ProductTypeDTO;
import it.traveldream.entities.Employee;
import it.traveldream.entities.Hotel;
import it.traveldream.entities.HotelRoom;
import it.traveldream.entities.Location;
import it.traveldream.entities.ProductType;
import it.traveldream.exceptions.InvalidProductException;
import it.traveldream.exceptions.LocationNotFoundException;
import it.traveldream.exceptions.ProductNotFoundException;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.annotation.security.RolesAllowed;
import javax.ejb.Local;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

/**
 * Session Bean implementation class HotelManager
 */
@Stateless
@Local(HotelManagerInterface.class)
@LocalBean
public class HotelManager implements HotelManagerInterface {

	@PersistenceContext
	private EntityManager em;
	private Logger log;
    /**
     * Default constructor. 
     */
    public HotelManager() {
        super();
        this.log = Logger.getLogger(this.getClass().getSimpleName());
    }

	@Override
	@RolesAllowed({Employee.role})
	public List<ProductTypeDTO> getProductTypes() {
		@SuppressWarnings("unchecked")
		List<ProductType> types = this.em.createNamedQuery("getAllProductTypes").getResultList();
		
		ArrayList<ProductTypeDTO> typesDTO = new ArrayList<>();
		for (ProductType t : types){
			typesDTO.add(t.toDTO());
		}
		
		return typesDTO;
	}

	@Override
	@RolesAllowed({Employee.role})
	public List<ProductDTO> getProductsByLocation(LocationDTO location)
			throws LocationNotFoundException {

		TransportMeanManager m = new TransportMeanManager();
		return m.getProductsByLocation(location);
	}

	@Override
	@RolesAllowed({Employee.role})
	public HotelDTO getProduct(ProductDTO product)
			throws ProductNotFoundException {
		Long productId = product.getProductId();
		Hotel hotel = this.em.find(Hotel.class, productId);
		if (hotel == null){
			this.log.severe("ERROR: Problem while fetching hotel" + product.getName());
			throw new ProductNotFoundException();
		}
		
		return hotel.toDTO();
	}

	@Override
	@RolesAllowed({Employee.role})
	public HotelDTO saveProduct(HotelDTO product)
			throws InvalidProductException {
		try{
			Location l = this.em.find(Location.class, product.getLocation().getLocationId());

			Hotel hotel = new Hotel(product.getName(), l);

			if (product.getRooms().size() != product.getPrices().size())
				throw new InvalidProductException();
			HashMap<HotelRoomType, HotelRoom> hotelRooms = new HashMap<>();
			for (HotelRoomType type : product.getRooms().keySet()){
				HotelRoom room = new HotelRoom(product.getPrices().get(type), 
						product.getRooms().get(type));
				hotelRooms.put(type, room);
			}
			hotel.setHotelRooms(hotelRooms);

			try{
				this.em.persist(hotel);
				this.em.flush();
			} catch (Exception e){
				this.log.severe("ERROR: Problem while creating hotel" + product.getName());
				throw new InvalidProductException();
			}

			return hotel.toDTO();
		} catch (Exception e){
			this.log.severe("ERROR: Creating hotel: "  + e.getMessage());
			throw new InvalidProductException();
		}
	}

	@Override
	@RolesAllowed({Employee.role})
	public void updateProduct(HotelDTO product)
			throws ProductNotFoundException, InvalidProductException {
		
		Hotel hotel = this.em.find(Hotel.class, product.getProductId());
		if (hotel == null)
			throw new ProductNotFoundException();
		try{
			Location l = this.em.find(Location.class, product.getLocation().getLocationId());
			hotel.setName(product.getName());
			hotel.setLocation(l);

			for (Map.Entry<HotelRoomType, HotelRoom> room : hotel.getHotelRooms().entrySet()){
				HotelRoomType type = room.getKey();
				Integer number = room.getValue().getRooms();
				BigDecimal price = room.getValue().getPrice();

				if (price != product.getPrices().get(type)){
					room.getValue().setPrice(product.getPrices().get(type));
				}
				if (number != product.getRooms().get(type)){
					room.getValue().setRooms(product.getRooms().get(type));
				}
			}

			this.em.merge(hotel);
			this.em.flush();
		} catch (Exception e){
			this.log.severe("ERROR: Problem while updating hotel" + product.getName());
			throw new InvalidProductException();
		}
	}

	@Override
	@RolesAllowed({Employee.role})
	public void deleteProduct(HotelDTO product) throws ProductNotFoundException {
		Hotel hotel = this.em.find(Hotel.class, product.getProductId());
		
		if (hotel == null)
			throw new ProductNotFoundException();
		try{
			this.em.remove(hotel);
			this.em.flush();
		} catch (Exception e){
			this.log.severe("ERROR: Problem while removing hotel" + product.getName());
			throw new RuntimeException();
		}
	}

	@Override
	@RolesAllowed({Employee.role})
	public List<HotelDTO> getHotelsByLocation(LocationDTO location)
			throws LocationNotFoundException {
		Location l = this.em.find(Location.class, location.getLocationId());
		if (l == null)
			throw new LocationNotFoundException();
		
		@SuppressWarnings("unchecked")
		List<Hotel> products = 
        		this.em.createNamedQuery("getAllHotelsInLocation").
        		setParameter("location", l).getResultList();
		
		ArrayList<HotelDTO> DTOs = new ArrayList<HotelDTO>();
		for (Hotel h : products){
			DTOs.add(h.toDTO());
		}
		return DTOs;
	}
}
